﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data.Linq;
using System.Data.Common;
using System.Text.RegularExpressions;
using System.Data.SqlClient;

namespace CoolCode.Data.Linq
{
    public static class DataContextExtensions
    {
        #region 
        /*
         * 数据库事务会带来锁，锁会降低数据库并发性，在某些“不巧”的情况下还会造成死锁。
         * 对于一些查询语句，我们完全可以显式为SELECT语句添加WITH (NOLOCK)选项来避免发出共享锁。
         * 因此我们现在扩展刚才的ExecuteQuery方法，使它接受一个withNoLock参数，表明是否需要为 SELECT添加WITH (NOLOCK)选项。
         */
        public static IEnumerable<T> ExecuteQuery<T>(this DataContext dataContext, IQueryable query, bool withNoLock)
        {
            DbCommand command = dataContext.GetCommand(query, withNoLock);

            dataContext.OpenConnection();

            using (DbDataReader reader = command.ExecuteReader())
            {
                return dataContext.Translate<T>(reader);
            }
        }

        private static Regex withNoLockRegex = new Regex(@"(] AS \[t\d+\])", RegexOptions.Compiled);

        private static string AddWithNoLock(string cmdText)
        {
            IEnumerable<Match> matches =
                withNoLockRegex.Matches(cmdText).Cast<Match>()
                .OrderByDescending(m => m.Index);
            foreach (Match m in matches)
            {
                int splitIndex = m.Index + m.Value.Length;
                cmdText = cmdText.Substring(0, splitIndex) + " WITH (NOLOCK)" + cmdText.Substring(splitIndex);
            }

            return cmdText;
        }

        private static void OpenConnection(this DataContext dataContext)
        {
            if (dataContext.Connection.State == System.Data.ConnectionState.Closed)
                dataContext.Connection.Open();
        }

        private static SqlCommand GetCommand(this DataContext dataContext, IQueryable query, bool withNoLock)
        {
            SqlCommand command = (SqlCommand)dataContext.GetCommand(query);

            if (withNoLock)
            {
                command.CommandText = AddWithNoLock(command.CommandText);
            }

            return command;
        }

        #endregion
    }


}
